Fox's Git Mirrors
docs/en/control-api.md 633735d797cc5f5d48cb2e22e8fd7cd743930daf (633735d7) Text, 11.00 KB
Control API
Purpose
T383838pkg/controlapi exposes a localhost JSON and WebSocket API so applications in any language can use Reticulum destinations, announces, links, and requests without embedding the Go transport stack.
The server is optional and disabled by default.
Architecture notes
The mesh is destinations, announces, and links between peers. No node is privileged on the wire. Upstream design intent: Zen of Reticulum.
The Control API is a local HTTP/WebSocket front end for one T383838reticulum-go process. It is not the mesh. Apps that treat this API as a required remote service reintroduce a single control host even when RNS routing stays peer-to-peer.
Appropriate uses
• Tools and UIs on the same host as the daemon
• App logic in another language while the daemon owns transport
• Lab or ops access on loopback (or a private network you fully control)
Avoid
• A public Control API endpoint that clients must use to participate
• Putting identity, routing, or app policy behind one always-on control host
• Large transfers via base64 T383838link.send_resource when rncp or in-process librns is available
• Binding off loopback and describing the result as decentralized because RNS is underneath
If the product fails when the Control API host is unreachable, the product depends on that host. Prefer peer destinations and links for application traffic. Keep this API on the machine that runs the node.
Enable in config
T282828
[reticulum]
enable_control_api = yes
rpc_key = <64 hex characters>
control_api_host = 127.0.0.1
control_api_port = 37430
# Optional extra listener. TCP stays enabled.
# control_api_socket = /run/reticulum-go/control.sock
rpc_key is a 32-byte value encoded as hex. The same key authenticates shared-instance RPC when configured.
Generate a key with a cryptographic random source. Example using OpenSSL:
T282828
openssl rand -hex 32
Authentication
All T383838/v1 routes require:
T282828
Authorization: Bearer <hex rpc_key>
Requests without a valid bearer token are rejected.
HTTP routes
┌────────┬──────────────────────────────────────────────────────┬──────────────────────────────────┐
│ Method │ Path │ Description │
├────────┼──────────────────────────────────────────────────────┼──────────────────────────────────┤
│ GET │ T383838/v1/health │ Liveness probe (process up, tra… │
│ GET │ T383838/v1/status │ Interface statistics, including… │
│ GET │ T383838/v1/paths │ Path table snapshot │
│ POST │ T383838/v1/sessions │ Create session (identity) │
│ DELETE │ T383838/v1/sessions/{id} │ Tear down session │
│ POST │ T383838/v1/sessions/{id}/destinations │ Register destination │
│ POST │ T383838/v1/sessions/{id}/destinations/{hash}/announce │ Send announce │
│ POST │ T383838/v1/sessions/{id}/destinations/{hash}/requests │ Bridge request path to WebSocket │
│ DELETE │ T383838/v1/sessions/{id}/destinations/{hash}/requests?path= │ Deregister request path │
│ POST │ T383838/v1/sessions/{id}/path/request │ Request path to destination │
│ GET │ T383838/v1/sessions/{id}/events │ WebSocket event stream │
└────────┴──────────────────────────────────────────────────────┴──────────────────────────────────┘
Lifecycle routes (Go node integration):
┌────────┬─────────────────────────────┬─────────────────────┐
│ Method │ Path │ Description │
├────────┼─────────────────────────────┼─────────────────────┤
│ POST │ T383838/v1/lifecycle/resume │ Resume after pause │
│ POST │ T383838/v1/lifecycle/pause │ Pause interfaces │
│ POST │ T383838/v1/lifecycle/refresh-paths │ Refresh stale paths │
└────────┴─────────────────────────────┴─────────────────────┘
Binary fields (hashes, app data, link payloads) are hex- or base64-encoded as documented in T383838pkg/controlapi/protocol.go.
Status integrity fields (Go daemon)
T383838GET /v1/status mirrors shared-instance interface stats. Against a Reticulum-Go daemon each interface object may include:
┌────────────────────────┬─────────────────────────────────────────────────────────────────────────┐
│ JSON field │ Meaning │
├────────────────────────┼─────────────────────────────────────────────────────────────────────────┤
│ ifac_fail │ IFAC verify failures │
│ hmac_fail │ Link HMAC failures │
│ announcesigfail │ Invalid announce signatures │
│ unpack_fail │ Packet unpack failures │
│ announce_dup │ Duplicate announce ignored │
│ pathrespsuppressed │ PATH_RESPONSE skipped (next hop is requestor) │
│ pathreqdup │ Duplicate path request tag ignored │
│ pathreqno_cache │ Known path without cached announce │
│ pathrespqueued_skip │ PATH_RESPONSE already queued for iface │
│ linkrelayunknown_iface │ Link relay dropped unknown source iface │
│ integrityfailrate │ Windowed fails / (fails + accepted) │
│ stale_closes │ Links closed after going stale │
│ linkstaleclose │ Same lifetime total as exposed on the iface │
│ keepalive_timeout │ Transitions into keepalive stale │
│ clients │ Spawned peer count (I2P parent) │
│ i2p_connectable │ Connectable I2P server tunnel enabled │
│ i2p_b32 │ Published T383838*.b32.i2p endpoint when connectable │
│ tunnelstate │ I2P peer tunnel label (T383838Creating Tunnel, T383838Tunnel Active, T383838Tunnel Unrespon… │
│ i2plasterror │ Last SAM dial or stream error text for an I2P peer │
└────────────────────────┴─────────────────────────────────────────────────────────────────────────┘
These counters are local observability only. They do not change packet accept or reject policy. For scored findings use T383838reticulum-go slow. For a full path and health dump use T383838reticulum-go snapshot. See Security, packet-debug, and CLI utilities.
Sessions
A session owns:
• One identity
• Destinations registered under that identity
• Links opened or accepted by the session
Typical flow:
T282828
POST /v1/sessions
-> session id
POST /v1/sessions/{id}/destinations
-> register app destination
POST /v1/sessions/{id}/destinations/{hash}/announce
-> publish announce
GET /v1/sessions/{id}/events (WebSocket)
-> subscribe to announces, links, requests
WebSocket events
Server to client JSON event type values:
┌────────────────────────┬───────────────────────────────────────────┐
│ Event │ Meaning │
├────────────────────────┼───────────────────────────────────────────┤
│ announce │ Remote announce received │
│ T383838link.established │ Link is active │
│ T383838link.failed │ Outbound link failed │
│ T383838link.data │ Data received on link │
│ T383838link.closed │ Link closed │
│ T383838link.remote_identified │ Peer identified on link │
│ T383838request.incoming │ Request arrived on registered path │
│ T383838request.response │ Outbound T383838link.request succeeded │
│ T383838request.failed │ Outbound T383838link.request failed or timed out │
│ T383838resource.started │ Resource transfer started │
│ T383838resource.concluded │ Resource transfer finished │
│ T383838command.error │ WebSocket command could not be applied │
└────────────────────────┴───────────────────────────────────────────┘
Client to server command type values:
┌─────────────────────┬────────────────────────────────────────────────────────────────────────────┐
│ Command │ Meaning │
├─────────────────────┼────────────────────────────────────────────────────────────────────────────┤
│ subscribe_announces │ Subscribe to announces. Empty filter means all. Non-empty filter must be … │
│ T383838link.open │ Open outbound link │
│ T383838link.send │ Send on link │
│ T383838link.close │ Close link │
│ T383838link.request │ Outbound request on established link │
│ T383838link.send_resource │ Send payload as a link resource (base64). Keep payloads small │
│ T383838link.identify │ Identify session identity on link │
│ T383838request.respond │ Answer a request. Optional filename for NomadNet T383838[name, bytes] │
└─────────────────────┴────────────────────────────────────────────────────────────────────────────┘
Full type definitions: T383838pkg/controlapi/protocol.go.
Links via API
Register a destination with link acceptance enabled for inbound links.
Outbound: send T383838link.open over the events WebSocket after the path exists (from announce or path request).
Both directions receive T383838link.established when ready, then T383838link.data for peer data.
Use T383838link.identify after the link is active. The peer receives T383838link.remote_identified.
Requests via API
Register a request path with T383838POST .../destinations/{hash}/requests. Incoming requests appear as T383838request.incoming. Respond with T383838request.respond before the handler timeout.
Outbound: after T383838link.established, send T383838link.request. Completion arrives as T383838request.response or T383838request.failed.
Handlers block the underlying link goroutine until response or timeout. Keep processing short.
Deregister with T383838DELETE .../requests?path=/your/path.
Resources via API
T383838link.send_resource mirrors librns minimal resource send. Expect T383838resource.started and T383838resource.concluded on the peer. Payloads are base64 over WebSocket, so large files are memory-heavy. Prefer rncp or in-process librns for bulk transfers.
Scope and caveats
This API is an application contract for destinations, announces, links, requests, identify, and minimal resources. It is not a full mirror of channels, stream buffers, resource cancel/progress, or mesh-admin ops (drop path, blackhole). Those stay on shared-instance RPC and CLI.
Control API T383838/v1 is independent of librns T383838RNS_API_VERSION. Additive JSON fields and new type strings are the compatibility model.
WebSocket event delivery is best-effort. A full client outbox drops events.
See Architecture notes when designing a product on top of this API.
Example client
T383838examples/control-client/client.py is a Python reference client for the API.
Security notes
• Default bind is loopback only
• Do not expose the control API to untrusted networks without additional protection
• Treat rpc_key as a secret comparable to an API token
• Binding off loopback for convenience fights the model in Architecture notes
• See Security
Implementation files
┌──────────────┬─────────────────────────┐
│ File │ Role │
├──────────────┼─────────────────────────┤
│ T383838server.go │ HTTP server and routing │
│ T383838session.go │ Session state │
│ T383838protocol.go │ Request and event types │
│ T383838ws.go │ WebSocket handling │
│ T383838auth.go │ Bearer validation │
│ T383838lifecycle.go │ Lifecycle routes │
└──────────────┴─────────────────────────┘
Daemon wiring: T383838cmd/reticulum-go/main.go starts T383838controlapi.Server when enabled.
Related documents
• Zen of Reticulum (upstream design intent)
• API reference for Go embedders using destinations and links in-process
• librns for in-process C ABI
• librns for in-process Odin bindings
• Examples
Dart and Flutter
Path: T383838bindings/dart/ (package rns_control).
Control API client
HTTP and WebSocket client for a local or LAN T383838reticulum-go daemon. Import T383838package:rns_control/rns_control.dart.
T282828
import 'package:rns_control/rns_control.dart';
final client = ControlClient(rpcKey: rpcKey);
final session = await client.createSession();
final events = client.openEvents(session.sessionId);
events.subscribeAnnounces();
Coverage includes health, status (with integrity counters), paths, sessions, destinations, announce, request handlers (register and deregister), lifecycle, outbound requests, resources, identify, and WebSocket commands or events. Authenticated WebSocket upgrades require T383838dart:io (Flutter mobile or desktop). Browser clients cannot set the Authorization header on WebSocket.
In-process FFI
For embedding without a daemon, use T383838package:rns_control/ffi.dart over librns on Linux, Android, and Windows. See librns Dart FFI.
T282828
task build-librns
task test-dart
# or
make -C bindings/dart test
Add to a Flutter app with a path dependency:
T282828
dependencies:
rns_control:
path: ../Reticulum-Go/bindings/dart
Served by rngit 1.5.2 - Generated in 0.03s